home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 44
/
Aminet 44 (2001)(GTI - Schatztruhe)[!][Aug 2001].iso
/
Aminet
/
misc
/
emu
/
p-interp.lha
/
p-interp-0.4
/
interpreter.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-06-11
|
51KB
|
2,357 lines
/*
P-Code interpreter (to run the apple pascal system)
Copyright (C) 2000 Mario Klebsch
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$Log: interpreter.c,v $
Revision 1.13 2001/06/07 18:54:40 mario
Abschneiden von zu grossen Werten beim STP(Store Packed)-Befehl. Ein
Bug? im eXamine-Kommando des Filers macht diese Änderung notwendig.
Revision 1.12 2001/06/06 23:00:27 mario
Anzahl der Elemente beim Compare von Byte- und Word-Arrays
ist B, nicht UB.
Revision 1.11 2001/05/29 22:52:31 mario
Quellenangabe für das Programm hinzugefügt
Revision 1.10 2001/05/27 19:02:04 mario
Opcodes in List() durch Mnemonics ersetzt.
Revision 1.9 2001/05/27 18:59:54 mario
Code zur Berechnung der Fehlerstelle in XeqError an die verschiedenen
Konfigurationsmöglichkeiten angepasst.
Revision 1.8 2001/05/27 16:31:46 mario
Debugger wieder eingebaut.
Revision 1.7 2001/05/27 16:21:48 mario
- Neue Kommandozeilenoption zum Tracen einer einzigen Prozedur
- Auch beim Disassemblieren von Segment 0 werden jetzt die
korrekten Prozedurnamen angezeigt.
Revision 1.6 2001/05/27 16:16:23 mario
Fehlerbehandlung patcht jetzt MISCINFO
Revision 1.5 2001/05/26 16:51:35 mario
Diverse Kommentare eingefügt, einige Funktionen umgruppiert.
Revision 1.4 2001/05/26 15:13:29 mario
Diverse kleine Fehler behoben, fehlende #includes, Labels ohne Statement
dahinter, ...
Revision 1.3 2001/05/21 20:50:55 mario
Trace nach stdout implementiert
Revision 1.2 2001/05/20 13:12:02 mario
CVS-Idents und Logs eingefügt
*/
#ident "$Id: interpreter.c,v 1.13 2001/06/07 18:54:40 mario Exp $";
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <setjmp.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include "version.h"
#include "psystem.h"
#include "Memory.h"
#include "Stack.h"
#include "Sets.h"
#include "Array.h"
#include "Diskio.h"
#include "Term.h"
#include "pcode.h"
#undef IXP_COMPATIBILITY
#undef TRACE_TRANSLATE
#define TIME_SCALE 1
word DisasmP (char *Buffer,
word SegNo,
word IpcBase,
word Ipc,
word JTab,
word Sp);
static FILE *TraceFile=NULL;
static byte TraceSeg;
static byte TraceProc;
#define APPLE_HEAP_BOT 0x0804
#define APPLE_KP_TOP 0xfe7c
#define APPLE_SEG0_LOAD_GAP 0x450a
#define APPLE_SYSCOM 0xbdde
#ifdef TRACE_TRANSLATE
#define KP_TOP 0xe000
#define HEAP_BOT 0x1000
#else
#define KP_TOP 0xfe80
#define HEAP_BOT 0x0200
#endif
#define NUMBER(a) (sizeof(a)/sizeof(a[0]))
typedef struct SegDict
{
int UseCount;
word OldKp;
word Seg;
word SegBase;
} SegDict_t;
SegDict_t SegDict[32];
#define MS_KP -1
#define MS_STAT 0
#define MS_DYN 1
#define MS_JTAB 2
#define MS_SEG 3
#define MS_IPC 4
#define MS_SP 5
#define MS_VAR 5 /* Var-Offset counts from 1.. */
#define MS_FRAME_SIZE 6
/* Official P-Machine registers */
word Sp;
word Ipc;
word IpcBase;
word Seg;
word JTab;
word Kp;
word Mp;
word Np;
word Base;
word Syscom;
/* Flags */
#ifndef WORD_MEMORY
int AppleCompatibility=0;
#endif
/* Additional Bookkeeping */
static word CurrentIpc;
static word BaseMp;
unsigned int Level=0;
unsigned int TraceLevel=0;
jmp_buf ProcessNextInstrunction;
#ifdef XXX
/* Zwei Funktionen, die früher einmal in Debug-Ausgaben benötigt
wurden, nun aber nicht mehr aufgerufen werden. Sie sind aber zu
schade, um schon in /dev/null entsorgt zu werden. */
char *PStr(word w)
{
static char Buffer[256];
int len=MemRdByte(w,0);
char *p=Buffer;
int i;
for (i=0; i<len; i++)
*p++=MemRdByte(w, i+1);
*p++='\0';
return(Buffer);
}
char *MultipleWords(word Addr, word Len)
{
static char Buffer[5*256+1];
char *p=Buffer;
while (Len--)
{
sprintf(p,",%04x", MemRd(Addr));
p+=strlen(p);
Addr=WordIndexed(Addr, 1);
}
return(Buffer);
}
void CheckCallStack(void)
{
int i;
word p=Mp;
for (i=0;i<Level;i++)
{
assert(p);
p=MemRd(WordIndexed(p, MS_DYN));
}
assert(p==(0xb000-4));
}
#endif
void warning(char *Msg, ...)
{
va_list ap;
char Buffer[512];
va_start(ap, Msg);
vsnprintf(Buffer, sizeof(Buffer), Msg, ap);
va_end(ap);
fprintf(stderr,"warning: %s\n", Buffer);
/* TraceLevel=0x7fff;*/
}
void DumpCore(void)
{
FILE *f;
if ((f=fopen("ucsd.core","w")))
{
MemDump(f,0,0xffff);
fclose(f);
}
else
warning("DumpCore: unable to create core dump");
}
void panic(char *Msg, ...)
{
va_list ap;
char Buffer[512];
TermClose();
va_start(ap, Msg);
vsnprintf(Buffer, sizeof(Buffer), Msg, ap);
va_end(ap);
fprintf(stderr,"panic: %s\n", Buffer);
DumpCore();
abort();
}
/* Convert to boolean. */
inline word Boolean(word i)
{
return(i?1:0);
}
void MoveLeft(word Dst, Integer DstOffset,
word Src, Integer SrcOffset, word Len)
{
while (Len--)
MemWrByte(Dst, DstOffset++,MemRdByte(Src, SrcOffset++));
}
void MoveRight(word Dst, Integer DstOffset,
word Src, Integer SrcOffset, word Len)
{
SrcOffset+=Len;
DstOffset+=Len;
while (Len--)
MemWrByte(Dst, --DstOffset, MemRdByte(Src,--SrcOffset));
}
word FetchB(void)
{
byte b;
b = MemRdByte(IpcBase, Ipc++);
if (b&0x80)
return ( (word)((b&0x7f)<<8) + (word)MemRdByte(IpcBase, Ipc++) );
else
return ( (word)b );
}
inline word FetchW(void)
{
word w;
w = MemRdByte(IpcBase, Ipc++);
w += (MemRdByte(IpcBase, Ipc++)<<8);
return(w);
}
inline word FetchUB(void)
{
return ( (word)MemRdByte(IpcBase, Ipc++) );
}
/* Dereference a self relocating pointer. Self relocating pointers are
used in the segment dictionary and in procedure activation records. */
static inline word SelfRelPtr(word Addr)
{
#ifdef WORD_MEMORY
return(Addr-MemRd(Addr)/2);
#else
return(Addr-MemRd(Addr));
#endif
}
/* Returns the number of procedures of a segment */
inline byte SegNumProc(word Seg)
{
return(MemRd(Seg)>>8);
}
/* Return the segment number of a segment */
inline byte SegNumber(word Seg)
{
return(MemRd(Seg)&0xff);
}
/* Returns a pointer to the activation record of a specified procedure
in a specified segment */
inline word Proc(word Seg, byte ProcNr)
{
PointerCheck(Seg);
if ((ProcNr<1) || (ProcNr>SegNumProc(Seg)) )
panic("Proc: Illegal Procedure Number %d",ProcNr);
return(SelfRelPtr(WordIndexed(Seg,-ProcNr)));
}
/* Returns the procedure number of a procedure */
inline signed char ProcNumber(word JTab)
{
PointerCheck(JTab);
return (MemRd(JTab)&0xff);
}
/* Returns the lex level of a procedure */
inline signed char ProcLexLevel(word JTab)
{
PointerCheck(JTab);
return (MemRd(JTab)>>8);
}
/* Returns a pointer to the first instruction of a procedure */
static inline word ProcBase(word JTab)
{
PointerCheck(JTab);
return (SelfRelPtr(WordIndexed(JTab,-1)));
}
/* Returns the byte offset to the exit code of a procedure. */
inline word ProcExitIpc(word JTab)
{
PointerCheck(JTab);
return (MemRd(WordIndexed(JTab,-1))-
MemRd(WordIndexed(JTab,-2))-2);
}
/* Returns the size of the parameters, which are passed to a
procedure. */
inline word ProcParamSize(word JTab)
{
PointerCheck(JTab);
return (MemRd(WordIndexed(JTab,-3)));
}
/* Returns the size of the storage a procedure needs for its local
variables. */
inline word ProcDataSize(word JTab)
{
PointerCheck(JTab);
return (MemRd(WordIndexed(JTab,-4)));
}
/* Returns a pointer to a local variable. */
inline word LocalAddr(word Offset)
{
return(WordIndexed(Mp, MS_VAR+Offset));
}
/* Returns a pointer to a global variable. */
inline word GlobalAddr(word Offset)
{
return(WordIndexed(Base,MS_VAR+Offset));
}
/* Traverse the static link chain. */
inline word Intermediate(byte Count)
{
word p;
for (p=Mp;Count;Count--)
p=MemRd(WordIndexed(p,MS_STAT));
return(p);
}
/* Returns a pointer to a variable of an enclosing procedure. */
inline word IntermediateAddr(word Offset, byte Count)
{
return( WordIndexed(Intermediate(Count), MS_VAR+Offset) );
}
/* Returns a pointer to a variable in a data segment (a global
variable in a UNIT) */
inline word ExtendedAddr(word Offset, byte SegNo)
{
assert(SegNo<NUMBER(SegDict));
return(WordIndexed(SegDict[SegNo].Seg, Offset));
}
/* calculates the target address of a jump operation. Positive
displacements perform relative jumps, negative displacements are
used as indices into the jump table. */
word jump(signed char disp)
{
if (disp >=0)
return(Ipc+disp);
disp=-disp;
#ifdef WORD_MEMORY
return(MemRd(WordIndexed(JTab, -1))+2-
(MemRd(JTab-disp/2)+disp));
#else
return(MemRd(WordIndexed(JTab, -1))+2-
(MemRd(JTab-disp)+disp));
#endif
}
/* Calculates the static link pointer for a procedure */
inline word StaticLink(word NewSeg, byte ProcNo)
{
word NewJTab=Proc(NewSeg, ProcNo);
if (!ProcNumber(NewJTab))
return(NIL);
return(Intermediate(ProcLexLevel(JTab)-
ProcLexLevel(NewJTab)+1));
}
/* load a segment. If a data segment is to be loaded, just allocate
storage on the stack */
void CspLoadSegment(byte SegNo)
{
assert (SegNo<NUMBER(SegDict));
if (!SegDict[SegNo].UseCount)
{
word SegUnit = MemRd(SEG_UNIT(SegNo));
word SegBlock = MemRd(SEG_BLOCK(SegNo));
word SegSize = MemRd(SEG_SIZE(SegNo));
assert (!(SegSize&1));
if (!SegSize)
XeqError(XNOPROC);
SegDict[SegNo].OldKp=Kp;
#ifdef WORD_MEMORY
Kp-=SegSize/2;
#else
Kp-=SegSize;
#endif
SegDict[SegNo].SegBase=Kp;
if (SegBlock) /* if a block number is specified, */
{ /* load a code segment. */
SegDict[SegNo].Seg=WordIndexed(SegDict[SegNo].OldKp,-1);
DiskRead(SegUnit, Kp, 0, SegSize, SegBlock);
if (MemRd(IORSLT))
XeqError(XSYIOER);
}
else /* it is a Data-Segment */
SegDict[SegNo].Seg=WordIndexed(Kp,-1);
}
SegDict[SegNo].UseCount++;
}
void CspUnloadSegment(byte SegNo)
{
SegDict[SegNo].UseCount--;
if (!SegDict[SegNo].UseCount)
{
Kp=SegDict[SegNo].OldKp;
SegDict[SegNo].OldKp = 0;
SegDict[SegNo].Seg = 0;
}
}
/* Clear the global directory pointer. */
void ClrGDirP(void)
{
word GDirP=MemRd(GDIRP);
if (GDirP!=NIL)
{
Np=GDirP;
MemWr(GDIRP, NIL);
}
}
/* check for a gap between heap and stack */
void StackCheck(void)
{
if (Np>=Kp)
{
MemWr(GDIRP, NIL);
Kp=0x8000;
Np=0x6200;
XeqError(XSTKOVR);
}
}
/* call a procedure. It does build a stack frame for the new procedure
and sets up all recisters of the p-machine. */
int call(word NewSeg, byte ProcNr, word StaticLink)
{
word NewJTab = Proc(NewSeg, ProcNr);
word DataSize = ProcDataSize(NewJTab);
word ParamSize = ProcParamSize(NewJTab);
word NewMp = WordIndexed(Kp, -(DataSize + ParamSize)/2);
if (!ProcNumber(NewJTab))
{
ProcessNative(NewJTab);
return(1);
}
assert(!(ParamSize&1));
MoveLeft(NewMp, 0, Sp, 0, ParamSize);
Sp=WordIndexed(Sp,ParamSize/2);
NewMp = WordIndexed(NewMp, -MS_FRAME_SIZE);
if (ProcLexLevel(NewJTab) <= 0)
{
Push(Base);
Base=NewMp;
MemWr(STKBASE,Base);
}
MemWr(WordIndexed(NewMp,MS_KP), Kp);
MemWr(WordIndexed(NewMp,MS_STAT), StaticLink);
MemWr(WordIndexed(NewMp,MS_DYN), Mp);
MemWr(WordIndexed(NewMp,MS_JTAB), JTab);
MemWr(WordIndexed(NewMp,MS_SEG), Seg);
MemWr(WordIndexed(NewMp,MS_IPC), Ipc);
MemWr(WordIndexed(NewMp,MS_SP), Sp);
Kp = WordIndexed(NewMp, -1); /* Kleiner Hack :-( */
Mp = NewMp;
Seg = NewSeg;
JTab = NewJTab;
MemWr(LASTMP, Mp);
#ifdef SEG
MemWr(SEG, Seg);
#endif
#ifdef JTAB
MemWr(JTAB, JTab);
#endif
IpcBase = ProcBase(JTab);
Ipc = 0;
Level++;
StackCheck();
return(0);
}
void ret(byte n)
{
word OldMp=Mp;
byte OldSegNo=SegNumber(Seg);
while (n>0)
Push(MemRd(LocalAddr(n--)));
Kp = MemRd(WordIndexed(OldMp,MS_KP));
Mp = MemRd(WordIndexed(OldMp,MS_DYN));
JTab = MemRd(WordIndexed(OldMp,MS_JTAB));
IpcBase = ProcBase(JTab);
Seg = MemRd(WordIndexed(OldMp,MS_SEG));
Ipc = MemRd(WordIndexed(OldMp,MS_IPC));
MemWr(LASTMP, Mp);
#ifdef SEG
MemWr(SEG, Seg);
#endif
#ifdef JTAB
MemWr(JTAB, JTab);
#endif
if (OldSegNo != SegNumber(Seg))
if (OldSegNo) /* Segment 0 wird nicht verwaltet */
CspUnloadSegment(OldSegNo);
Level--;
StackCheck();
}
/* An execution error has occured. Resum executon at segment 2
procedure 1, the system error handler */
void XeqError(word err)
{
static int Flag=0;
word NewSeg=SegDict[0].Seg;
if (Flag)
panic("XeqError: recursion");
Flag++;
MemWr(XEQERR, err);
#ifdef BOMBPROC
MemWr(BOMBPROC, ProcNumber(JTab));
#endif
#ifdef BOMBSEG
MemWr(BOMBSEG, SegNumber(Seg));
#endif
#if 0
#ifndef WORD_MEMORY
/* The bytewise interpreter used in Apple Pascal did use Pointers
for the Ipc. Addinf the Offset to IpcBase id sufficient to
emulate this behavoir. */
MemWr(BOMBIPC, IpcBase+CurrentIpc);
#else
/* In my wordwise interpreter, I do calculate this value in a way,
that the Apple Pascal error printing routine does print the
correct result. But, this value for BOMBIPC is not the location
of the failed instruction. */
MemWr(BOMBIPC,JTab-MemRd(WordIndexed(JTab,-1))-2+CurrentIpc);
#endif
MemWr(MISCINFO, MemRd(MISCINFO) & ~(1<<10));
#else
/* Early versions of Apple Pascal do contain the code to directly
print this offset. A bit in system.miscinfo is checked in the
system error handler to see, wether BOMBIPC does contain a
pointer or an offset. */
MemWr(BOMBIPC, CurrentIpc);
MemWr(MISCINFO, MemRd(MISCINFO) | 1<<10);
#endif
call(NewSeg, 2, BaseMp );
MemWr(BOMBP, Mp);
/* This code can be used to anter debugging upon entry of the system
error handler. It probably is only usefull to debug the system
error handler. */
#ifdef XXX
TraceLevel=0x7fff;
warning ("XeqError(%d)", err);
#endif
Flag--;
longjmp(ProcessNextInstrunction, 0);
}
/****************************************************************************/
/* */
/* P-debugger stuff. */
/* */
/* Dump memory in decimal and in hex. Used to dump the evaluation stack */
void ShowMem(word Start, word End)
{
for ( ;Start<End; Start=WordIndexed(Start,1))
fprintf(stderr," %d(%x)",MemRd(Start), MemRd(Start));
fprintf(stderr,"\n");
}
/* Disassemble a procedure */
void List(FILE *out, int SegNo, word JTab)
{
word IpcBase=ProcBase(JTab);
word Ipc=0;
char Buffer[1024];
fprintf(out, "Params: %d, Vars: %d\n",
ProcParamSize(JTab)/2,
ProcDataSize(JTab)/2);
while (WordIndexed(IpcBase, Ipc/2)<JTab)
{
word OpCode=MemRdByte(IpcBase, Ipc);
sprintf(Buffer,"%d: ", Ipc);
Ipc=DisasmP(Buffer+strlen(Buffer), SegNo, IpcBase, Ipc, JTab, 0);
fprintf(out, "%s\n", Buffer);
if ( (OpCode==RNP) ||
(OpCode==RBP) ||
(OpCode==XIT) )
return;
}
}
void Debugger(void)
{
char prompt[64];
char Buffer[256];
int from,to;
char Buf[10];
char *line;
FILE *out;
char *mode;
int (*close_method)(FILE*);
if (Level>TraceLevel)
return;
TraceLevel=0x7fff;
DisasmP(Buffer, SegNumber(Seg), IpcBase, Ipc, JTab, Sp);
snprintf(prompt, sizeof(prompt), "s%d, p%d, %4d: %s > ", SegNumber(Seg), ProcNumber(JTab),
CurrentIpc, Buffer);
do
{
Buffer[0]='\0';
fprintf(stderr,"%s", prompt);
fgets(Buffer, sizeof(Buffer)-1, stdin);
close_method = NULL;
out = NULL;
line=Buffer;
while (*line)
if ( (*line == '|')|| (*line == '>') )
break;
else
line++;
if (*line == '|')
{
*line='\0';
line++;
while (*line)
if (isspace(*line))
line++;
else
break;
out = popen(line, "w");
close_method = pclose;
}
else if (*line == '>')
{
*line='\0';
line++;
if (*line == '>')
{
line++;
mode="a";
}
else
mode="w";
while (*line)
if (isspace(*line))
line++;
else
break;
out = fopen(line, mode);
close_method = fclose;
}
if (!out)
{
close_method = NULL;
out=stderr;
}
switch (Buffer[0])
{
case 'p': /* print stack */
fprintf(stderr,"Stack:");
ShowMem(Sp,SP_TOP);
break;
case 'd':
switch (sscanf(Buffer, "%10s %x %x", Buf, &from, &to))
{
/*
case 1:
from=NextDumpAddr;
*/
case 2:
to=from+0x80;
case 3:
MemDump(out, from,to);
break;
default:
fprintf(stderr,"d <from> [<to>]\n");
}
break;
case 'l':
{
int SegNo;
int ProcNo;
switch (sscanf(Buffer, "%10s %d %d", Buf, &SegNo, &ProcNo))
{
case 2:
ProcNo=SegNo;
SegNo=SegNumber(Seg);
case 3:
if (SegNo<NUMBER(SegDict))
{
CspLoadSegment(SegNo);
List(out, SegNo, Proc(SegDict[SegNo].Seg, ProcNo));
CspUnloadSegment(SegNo);
}
break;
default:
fprintf(stderr,"l [<SegNo>] <ProcNo>\n");
}
}
break;
case 't':
{
word s=Seg;
word j=JTab;
word m=Mp;
word i=Ipc;
while (1)
{
word w;
fprintf(out,"\ns%d, p%d, %4d:\n",
SegNumber(s), ProcNumber(j), i);
w=WordIndexed(m, MS_VAR);
MemDump(out, w,w+ProcParamSize(j)+ProcDataSize(j));
if (ProcLexLevel(j)<0)
break;
j = MemRd(WordIndexed(m,MS_JTAB));
s = MemRd(WordIndexed(m,MS_SEG));
i = MemRd(WordIndexed(m,MS_IPC));
m = MemRd(WordIndexed(m,MS_DYN));
}
}
MemDump(out, Kp, 0xb000);
break;
case 'v':
MemDump(out, WordIndexed(Mp,MS_VAR), WordIndexed(Mp,MS_VAR)+ProcDataSize(JTab)+ProcParamSize(JTab));
break;
case 'g':
TraceLevel=0;
return;
case 'n':
TraceLevel=Level;
return;
case 'f':
TraceLevel=Level-1;
return;
case 'r':
fprintf(out,"Sp=%04x, Kp=%04x, Mp=%04x, Base=%04x, Seg=%04x, JTab=%04x, Np=%04x\n",
Sp, Kp, Mp, Base, Seg, JTab, Np);
break;
case 'q':
if (TraceFile)
fclose(TraceFile);
exit(0);
break;
}
if (close_method && out)
{
close_method(out);
close_method=NULL;
out=NULL;
}
} while (Buffer[0]!='\n');
}
/****************************************************************************/
/* */
/* P-tracing stuff. */
/* */
/* To compare traces with byte and word architecture, this routine
tries to 'normalize' the value of pointers. Of course, the
assumtions are not always true, but the diffs get a lot shorter
using this translation. :-) */
inline word Translate(word Value)
{
#ifdef TRACE_TRANSLATE
#ifdef WORD_MEMORY
if (Value>KP_TOP)
;
else if (Value>0x8000)
Value = (Value-KP_TOP)*2+KP_TOP;
else if (Value>0x7f00)
;
else if (Value >HEAP_BOT)
Value = (Value-HEAP_BOT)*2+HEAP_BOT;
#endif
#endif
return(Value);
}
void Tracer(void)
{
char Buffer[64000];
char StackBuf[1024];
char *p=StackBuf;
word w=Sp;
*p='\0';
while (w<SP_TOP)
{
word Value=MemRd(w);
sprintf(p,"%04x ",Translate(Value));
p+=strlen(p);
w=WordIndexed(w,1);
}
DisasmP(Buffer, MemRd(Seg)&0xff, IpcBase, Ipc, JTab, Sp);
fprintf(TraceFile,"s%d p%d o%d %s Stack: %s\n",
MemRd(Seg)&0xff, MemRd(JTab)&0xff, Ipc, Buffer, StackBuf);
fflush(TraceFile);
}
void SetTrace(char *list)
{
int i,j;
char *p;
p=strchr(list, ',');
switch(sscanf(list, "%d,%d", &i, &j))
{
case 1:
TraceProc=i;
break;
case 2:
TraceSeg=i;
TraceProc=j;
break;
default:
fprintf(stderr,"invalid trace flags\n");
exit(1);
}
}
/****************************************************************************/
/* */
/* The P-machine itself. */
/* */
int AppleHack1(void)
{
word Save0=Ipc;
if (FetchUB() == 145) /* NGI */
{
word OpCode=FetchUB();
if (OpCode == 171) /* SRO */
{
word Var=FetchB(); /* Parameter SRO */
OpCode=FetchUB();
if ((((OpCode == 169) && /* LDO n */
(FetchB() == Var)) ||
((Var>=1) && (Var<=16) &&
(OpCode == 231+Var))) && /* SLDO n */
(FetchUB() == 0) && /* SLCD 0 */
(FetchUB() == 190) ) /* LDB */
return(1);
}
else if (OpCode == 204) /* STL */
{
word Var=FetchB(); /* Parameter STL */
OpCode=FetchUB();
if ((((OpCode == 202) && /* LDL n */
(FetchB() == Var)) ||
((Var>=1) && (Var<=16) &&
(OpCode == 215+Var))) && /* SLDL n */
(FetchUB() == 0) && /* SLCD 0 */
(FetchUB() == 190) ) /* LDB */
return(1);
}
}
Ipc=Save0;
return(0);
}
int AppleHack2(void)
{
word Save=Ipc;
word Var;
if ( (FetchUB() == 145) && /* NGI */
(FetchUB() == 171) && /* SRO */
(Var=FetchB()) && /* Parameter SRO */
(FetchUB() == 169) && /* LDO n */
(FetchB() == Var) &&
(FetchUB() == 6) && /* SLDC 6 */
(FetchUB() == 192) && /* IXP 16,1 */
(FetchUB() == 16) &&
(FetchUB() == 1) &&
(FetchUB() == 186)) /* LDP */
return(1);
Ipc=Save;
return(0);
}
void Processor(void)
{
byte Opcode;
word w;
float f;
register word p1, p2;
setjmp(ProcessNextInstrunction);
for ( ; /* ever */ ; )
{
/* CheckCallStack(); */
if (TraceFile)
if (!TraceProc ||
( (TraceProc == ProcNumber(JTab)) &&
(TraceSeg == SegNumber(Seg) ) ) )
Tracer();
Debugger();
CurrentIpc = Ipc;
Opcode = FetchUB(); /* fetch next instruction */
switch (Opcode)
{
/* One-word load and stores constant */
case SLDC_0: case SLDC_1: case SLDC_2: case SLDC_3:
case SLDC_4: case SLDC_5: case SLDC_6: case SLDC_7:
case SLDC_8: case SLDC_9: case SLDC_10: case SLDC_11:
case SLDC_12: case SLDC_13: case SLDC_14: case SLDC_15:
case SLDC_16: case SLDC_17: case SLDC_18: case SLDC_19:
case SLDC_20: case SLDC_21: case SLDC_22: case SLDC_23:
case SLDC_24: case SLDC_25: case SLDC_26: case SLDC_27:
case SLDC_28: case SLDC_29: case SLDC_30: case SLDC_31:
case SLDC_32: case SLDC_33: case SLDC_34: case SLDC_35:
case SLDC_36: case SLDC_37: case SLDC_38: case SLDC_39:
case SLDC_40: case SLDC_41: case SLDC_42: case SLDC_43:
case SLDC_44: case SLDC_45: case SLDC_46: case SLDC_47:
case SLDC_48: case SLDC_49: case SLDC_50: case SLDC_51:
case SLDC_52: case SLDC_53: case SLDC_54: case SLDC_55:
case SLDC_56: case SLDC_57: case SLDC_58: case SLDC_59:
case SLDC_60: case SLDC_61: case SLDC_62: case SLDC_63:
case SLDC_64: case SLDC_65: case SLDC_66: case SLDC_67:
case SLDC_68: case SLDC_69: case SLDC_70: case SLDC_71:
case SLDC_72: case SLDC_73: case SLDC_74: case SLDC_75:
case SLDC_76: case SLDC_77: case SLDC_78: case SLDC_79:
case SLDC_80: case SLDC_81: case SLDC_82: case SLDC_83:
case SLDC_84: case SLDC_85: case SLDC_86: case SLDC_87:
case SLDC_88: case SLDC_89: case SLDC_90: case SLDC_91:
case SLDC_92: case SLDC_93: case SLDC_94: case SLDC_95:
case SLDC_96: case SLDC_97: case SLDC_98: case SLDC_99:
case SLDC_100: case SLDC_101: case SLDC_102: case SLDC_103:
case SLDC_104: case SLDC_105: case SLDC_106: case SLDC_107:
case SLDC_108: case SLDC_109: case SLDC_110: case SLDC_111:
case SLDC_112: case SLDC_113: case SLDC_114: case SLDC_115:
case SLDC_116: case SLDC_117: case SLDC_118: case SLDC_119:
case SLDC_120: case SLDC_121: case SLDC_122: case SLDC_123:
case SLDC_124: case SLDC_125: case SLDC_126: case SLDC_127:
Push( Opcode-SLDC_0 ); /* SLDC 0..127 Short LoaD Constant */
break;
case LDCN: /* LDCN LoaD Constant Nil */
Push( NIL );
break;
case LDCI: /* LDCI LoaD Constant Integer */
p1=FetchW();
if (p1 == 16607) /* Apple-Hack */
if (AppleHack1())
Push(4);
else
Push( p1 );
else if (p1 == 16606)
if (AppleHack2())
Push(Boolean(0));
else
Push( p1 );
else
Push( p1 );
break;
/* One-word load and stores local */
/* SLDL Short LoaD Local 1..16 */
case SLDL_1: case SLDL_2: case SLDL_3: case SLDL_4:
case SLDL_5: case SLDL_6: case SLDL_7: case SLDL_8:
case SLDL_9: case SLDL_10: case SLDL_11: case SLDL_12:
case SLDL_13: case SLDL_14: case SLDL_15: case SLDL_16:
Push( MemRd( LocalAddr( Opcode-SLDL_1+1 )));
break;
case LDL: /* LDL LoaD Local */
Push( MemRd( LocalAddr( FetchB() ) ) );
break;
case LLA: /* LLA Load Local Addres */
Push( LocalAddr( FetchB() ) );
break;
case STL: /* STL STore Local */
MemWr( LocalAddr( FetchB() ), Pop() );
break;
/* One-word load and stores global */
/* SLDO Short LoaD glObal word */
case SLDO_1: case SLDO_2: case SLDO_3: case SLDO_4:
case SLDO_5: case SLDO_6: case SLDO_7: case SLDO_8:
case SLDO_9: case SLDO_10: case SLDO_11: case SLDO_12:
case SLDO_13: case SLDO_14: case SLDO_15: case SLDO_16:
Push( MemRd( GlobalAddr( Opcode-SLDO_1+1 )));
break;
case LDO: /* LDO LoaD glObal */
Push( MemRd( GlobalAddr( FetchB() )));
break;
case LAO: /* LAO Load Address glObal */
Push( GlobalAddr( FetchB() ));
break;
case SRO: /* SRO StoRe glObal */
MemWr( GlobalAddr( FetchB() ), Pop() );
break;
/* One-word load and stores intermediate */
case LOD: /* LOD LOaD */
p1 = FetchUB(); Push( MemRd( IntermediateAddr( FetchB(), p1 )));
break;
case LDA: /* LDA LOad Addres */
p1 = FetchUB(); Push( IntermediateAddr( FetchB(), p1 ));
break;
case STR: /* STR StoRe */
p1 = FetchUB(); MemWr( IntermediateAddr( FetchB(), p1 ), Pop());
break;
/* One-word load and stores indirect */
/* SIND Short INDirect */
case SIND_0: case SIND_1: case SIND_2: case SIND_3:
case SIND_4: case SIND_5: case SIND_6: case SIND_7:
Push( MemRd( WordIndexed( Pop(), Opcode-SIND_0 )));
break;
case IND: /* IND INDirect */
Push( MemRd( WordIndexed( Pop(), FetchB() )));
break;
case STO: /* STO STOre indirect */
p1 = Pop(); MemWr( Pop(), p1 );
break;
/* One-word load and stores indirect */
case LDE: /* LDE LoaD Extended */
p1 = FetchUB(); Push( MemRd( ExtendedAddr( FetchB(), p1 )));
break;
case LAE: /* LAE Load Addres Extended */
p1 = FetchUB(); Push( ExtendedAddr( FetchB(), p1 ));
break;
case STE: /* STE STore Extended */
p1 = FetchUB(); MemWr( ExtendedAddr( FetchB(), p1 ), Pop() );
break;
/* multiple-word loads and stores */
case LDC:
p1=FetchUB();
Ipc=(Ipc+1)&(~1); /* Nur auf Wortgrenze erlaubt */
#ifdef WORD_MEMORY
w=IpcBase+Ipc/2;
#else
w=IpcBase+Ipc;
#endif
while (p1--)
Push( FetchW() );
break;
case LDM:
p1=FetchUB();
w=Pop();
while (p1--)
Push( MemRd( WordIndexed( w, p1 )));
break;
case STM:
p1=FetchUB();
w=MemRd(WordIndexed(Sp,p1));
while (p1--)
{
MemWr(w, Pop());
w=WordIndexed(w,1);
}
Pop();
break;
/* byte array handling */
case LDB:
w=Pop(); Push(MemRdByte(Pop(), w));
break;
case STB:
p1=Pop(); w=Pop(); MemWrByte(Pop(), w, p1);
break;
/* string handling */
case LSA:
assert(!(Ipc&1));
Push( WordIndexed( IpcBase, Ipc/2 ) );
Ipc += FetchUB();
break;
case SAS:
p1=FetchUB();
if ((w=Pop())&0xff00)
{ /* copy String */
byte Len=MemRdByte(w, 0);
word Dest=Pop();
if (Len>p1)
XeqError(XS2LONG);
MoveLeft(Dest, 0, w, 0, Len+1);
}
else
{ /* store Char */
word Dest=Pop();
MemWrByte(Dest, 0, 1); /* make string of len 1 */
MemWrByte(Dest, 1, w); /* containing char on stack */
}
break;
case IXS:
p1=Pop(); p2=Pop();
Push(p2); Push(p1);
if (p1>MemRdByte(p2, 0))
XeqError(XINVNDX);
break;
/* record and array handling */
case MOV:
p1=FetchB();
{
word Src=Pop();
word Dst=Pop();
while (p1--)
{
MemWr(Dst, MemRd(Src));
Dst=WordIndexed(Dst,1);
Src=WordIndexed(Src,1);
}
}
break;
case INC:
Push( WordIndexed( Pop(), FetchB() ) );
break;
case IXA:
w=Pop();
Push( WordIndexed( Pop(), w*FetchB() ) );
break;
case IXP:
p1 = FetchUB(); p2 = FetchUB(); w=Pop();
Push(WordIndexed(Pop(),w/p1)); /* Address */
Push(p2);
Push((w%p1)*p2
#ifdef IXP_COMPATIBILITY
*0x101
#endif
);
break;
case LPA:
p1=FetchB();
#ifdef WORD_MEMORY
Push(IpcBase+Ipc/2);
#else
Push(IpcBase+Ipc);
#endif
Ipc+=p1;
break;
case LDP:
{
word Offset=Pop()&0xff;
word Size=Pop();
word Addr=Pop();
if (Offset+Size>16)
{
warning("LDP: Offset(%d)+Size(%d) > Bits per word",
Offset, Size);
XeqError(XINVNDX);
}
Push((MemRd(Addr)>>Offset)&((1<<Size)-1));
}
break;
case STP:
w=Pop();
{
word Offset=Pop()&0xff;
word Size=Pop();
word Addr=Pop();
if (Offset+Size>16)
{
warning("STP: Offset(%d)+Size(%d) > Bits per word",
Offset, Size);
XeqError(XINVNDX);
}
w &= (1<<Size)-1;
MemWr(Addr,
(MemRd(Addr) & ~(((1<<Size)-1)<<Offset)) | (w<<Offset));
}
break;
/* TOS arithmetic: integers */
case ABI: /* ABI ABsolute Integer */
Push( abs( PopInteger()));
break;
case ADI:
Push( PopInteger() + PopInteger() );
break;
case NGI:
Push( -PopInteger() );
break;
case SBI:
{
Integer i=PopInteger();
Push( PopInteger()-i );
}
break;
case MPI:
Push( PopInteger() * PopInteger() );
break;
case SQI:
{
Integer i=PopInteger();
Push( i*i );
}
break;
case DVI:
{
Integer i=PopInteger();
if (!i)
XeqError(XDIVZER);
Push( PopInteger() / i );
}
break;
case MODI:
{
Integer i=PopInteger();
if (!i)
XeqError(XDIVZER);
Push( PopInteger() % i );
}
break;
case CHK:
{
Integer Upper=PopInteger();
Integer Lower=PopInteger();
Integer Value=PopInteger();
Push(Value);
if ( (Value>Upper) || (Value<Lower) )
XeqError(XINVNDX);
}
break;
case EQUI:
{
Integer i=PopInteger();
Push ( Boolean ( PopInteger() == i ) );
}
break;
case NEQI:
{
Integer i=PopInteger();
Push ( Boolean ( PopInteger() != i ) );
}
break;
case LEQI:
{
Integer i=PopInteger();
Push ( Boolean ( PopInteger() <= i ) );
}
break;
case LESI:
{
Integer i=PopInteger();
Push ( Boolean ( PopInteger() < i ) );
}
break;
case GEQI:
{
Integer i=PopInteger();
Push ( Boolean ( PopInteger() >= i ) );
}
break;
case GRTI:
{
Integer i=PopInteger();
Push ( Boolean ( PopInteger() > i ) );
}
break;
/* TOS arithmetic: reals */
case FLT:
PushReal(PopInteger());
break;
case FLO:
f=PopReal();
PushReal(PopInteger());
PushReal(f);
break;
case ABR:
PushReal(fabs(PopReal()));
break;
case ADR:
PushReal(PopReal()+PopReal());
break;
case NGR:
PushReal(-PopReal());
break;
case SBR:
f=PopReal();
PushReal(PopReal()-f);
break;
case MPR:
PushReal(PopReal()*PopReal());
break;
case SQR:
f=PopReal();
PushReal(f*f);
break;
case DVR:
if ((f=PopReal())==0)
XeqError(XDIVZER);
PushReal(PopReal()/f);
break;
case EQU:
switch(FetchUB())
{
case 2:
f=PopReal(); Push(Boolean( PopReal() == f ));
break;
case 4:
w=Pop(); Push(Boolean(StrCmp(Pop(), w) == 0));
break;
case 6:
Push(Boolean((Pop()&1)==(Pop()&1)));
break;
case 8:
{
Set_t Set1;
Set_t Set2;
SetPop(&Set1);
SetPop(&Set2);
Push(Boolean(SetCmp(&Set1, &Set2) == 0));
break;
}
case 10:
w=Pop(); Push(Boolean(ByteCmp(Pop(), w, FetchB() ) == 0));
break;
case 12:
p1=FetchB();
w=Pop();
Push(Boolean(WordCmp(Pop(), w, p1) == 0));
break;
default:
XeqError(XNOTIMP);
}
break;
case NEQ:
switch(FetchUB())
{
case 2:
f=PopReal(); Push(Boolean( PopReal() != f ));
break;
case 4:
w=Pop(); Push(Boolean(StrCmp(Pop(), w) != 0));
break;
case 6:
Push(Boolean((Pop()&1)!=(Pop()&1)));
break;
case 8:
{
Set_t Set1;
Set_t Set2;
SetPop(&Set1);
SetPop(&Set2);
Push(Boolean(SetCmp(&Set1, &Set2) != 0));
break;
}
case 10:
w=Pop(); Push(Boolean(ByteCmp(Pop(), w, FetchB() ) != 0));
break;
case 12:
p1=FetchB();
w=Pop();
Push(Boolean(WordCmp(Pop(), w, p1) != 0));
break;
default:
XeqError(XNOTIMP);
}
break;
case LEQ:
switch(FetchUB())
{
case 2:
f=PopReal(); Push(Boolean( PopReal() <= f ));
break;
case 4:
w=Pop(); Push(Boolean(StrCmp(Pop(), w) <= 0) );
break;
case 6:
w=Pop()&1; Push(Boolean((Pop()&1) <= w));
break;
case 8:
{
Set_t Set1;
Set_t Set2;
SetPop(&Set1);
SetPop(&Set2);
Push(Boolean(SetIsSubset(&Set1, &Set2)));
break;
}
break;
case 10:
w=Pop(); Push(Boolean(ByteCmp(Pop(), w, FetchB()) <= 0));
break;
default:
XeqError(XNOTIMP);
}
break;
case LES:
switch(FetchUB())
{
case 2:
f=PopReal(); Push(Boolean( PopReal() < f ));
break;
case 4:
w=Pop(); Push(Boolean(StrCmp(Pop(), w) < 0) );
break;
case 6:
w=Pop()&1; Push(Boolean((Pop()&1) < w));
break;
case 10:
w=Pop(); Push(Boolean(ByteCmp(Pop(), w, FetchB()) < 0));
break;
default:
XeqError(XNOTIMP);
}
break;
case GEQ:
switch(FetchUB())
{
case 2:
f=PopReal(); Push(Boolean( PopReal() >= f ));
break;
case 4:
w=Pop(); Push(Boolean(StrCmp(Pop(), w) >= 0) );
break;
case 6:
w=Pop()&1; Push(Boolean((Pop()&1) >= w));
break;
case 8:
{
Set_t Set1;
Set_t Set2;
SetPop(&Set1);
SetPop(&Set2);
Push(Boolean(SetIsSubset(&Set2, &Set1)));
break;
}
break;
case 10:
w=Pop(); Push(Boolean(ByteCmp(Pop(), w, FetchB()) >= 0));
break;
default:
XeqError(XNOTIMP);
}
break;
case GRT:
switch(FetchUB())
{
case 2:
f=PopReal(); Push(Boolean( PopReal() > f ));
break;
case 4:
w=Pop(); Push(Boolean(StrCmp(Pop(), w) > 0) );
break;
case 6:
w=Pop()&1; Push(Boolean((Pop()&1) > w));
break;
case 10:
w=Pop(); Push(Boolean(ByteCmp(Pop(), w, FetchB()) > 0));
break;
default:
XeqError(XNOTIMP);
}
break;
/* TOS arithmetic: logical */
case LAND:
Push( Pop() & Pop() );
break;
case LOR:
Push( Pop() | Pop() );
break;
case LNOT:
Push( ~Pop() );
break;
/* Sets */
case ADJ:
p1=FetchUB();
w=MemRd(Sp);
if (p1!=w)
{
Set_t Buf;
SetPop(&Buf);
SetAdj(&Buf, p1);
SetPush(&Buf);
}
if (p1!=Pop())
panic("adj failure");
break;
case SGS:
w=Pop();
if (w<512)
{
int Size=(w+16)/16;
word Addr;
int i;
for (i=0;i<Size;i++)
Push(0);
Addr=WordIndexed(Sp, w/16);
MemWr(Addr, MemRd(Addr)|(1<<w%16));
Push(Size);
}
else
XeqError(XINVNDX);
break;
case SRS:
p1=Pop();
p2=Pop();
if ((p1<512) && (p1<512) )
{
if (p2>p1)
Push(0);
else
{
int Size=(p1+16)/16;
word Addr;
int i;
for (i=0;i<Size;i++)
Push(0);
while (p2<=p1)
{
Addr=WordIndexed(Sp, p2/16);
MemWr(Addr, MemRd(Addr)|(1<<p2%16));
p2++;
}
Push(Size);
}
}
else
XeqError(XINVNDX);
break;
case INN:
{
word Size;
word Addr;
word Val;
Size=Pop();
Addr=Sp;
Sp=WordIndexed(Sp,Size);
Val=Pop();
if (Val>=16*Size)
Push(Boolean(0));
else
Push(Boolean(MemRd(WordIndexed(Addr,(Val/16))) &
(1<<(Val%16))));
}
break;
case UNI:
{
int i;
word Size;
Set_t Set;
SetPop(&Set);
Size=Pop();
if (Size>Set.Size)
SetAdj(&Set, Size);
for (i=0; i<Size; i++)
Set.Data[i]|=Pop();
SetPush(&Set);
}
break;
case INT:
{
int i;
word Size;
Set_t Set;
SetPop(&Set);
Size=Pop();
if (Size>Set.Size)
SetAdj(&Set, Size);
for (i=0; i<Size; i++)
Set.Data[i]&=Pop();
while(i<Set.Size)
Set.Data[i++]=0;
SetPush(&Set);
}
break;
case DIF:
{
int i;
word Size;
Set_t Set;
SetPop(&Set);
Size=Pop();
if (Size>Set.Size)
SetAdj(&Set, Size);
for (i=0; i<Size; i++)
Set.Data[i] = Pop() & ~Set.Data[i];
while (i<Set.Size)
Set.Data[i]=0;
SetPush(&Set);
}
break;
/* jumps */
case UJP:
w=jump((signed char)FetchUB());
if ( ( Ipc-w == 5 ) && /* check for endless loop */
( MemRdByte(IpcBase, w ) == SLDC_1 ) &&
( MemRdByte(IpcBase, w+1) == FJP ) &&
( MemRdByte(IpcBase, w+2) == 2 ) )
sleep(1); /* reduce processor load */
Ipc=w;
break;
case FJP:
p1=FetchUB();
if (! (Pop()&1) )
{
w=jump((signed char)p1);
if ( ( Ipc-w == 3 ) && /* check for endless loop */
( MemRdByte(IpcBase, w ) == SLDC_0 ) )
sleep(1); /* reduce processor load */
Ipc=w;
}
break;
case EFJ:
p1=FetchUB();
if ( Pop() != Pop() )
Ipc=jump((signed char)p1);
break;
case NFJ:
p1=FetchUB();
if ( Pop() == Pop() )
Ipc=jump((signed char)p1);
break;
case XJP:
Ipc=(Ipc+1)&(~1);
p1=FetchW(); p2=FetchW();
w=Pop();
if ( (w>=p1) && (w<=p2) )
{
Ipc=Ipc+2*(w-p1)+2;
Ipc-=MemRd( WordIndexed( IpcBase, Ipc/2) );
}
break;
/* procedure and function calls */
case CLP:
call(Seg, FetchUB(), Mp);
break;
case CGP:
call(Seg, FetchUB(), Base);
break;
case CIP:
p1=FetchUB();
call(Seg, p1, StaticLink(Seg, p1));
break;
case CBP:
call(Seg, FetchUB(), BaseMp);
break;
case CXP:
p1=FetchUB(); p2=FetchUB();
if (p1) /* Nicht bei Segment 0 */
CspLoadSegment(p1);
w=SegDict[p1].Seg;
if (call(w, p2, StaticLink(w, p2) ))
CspUnloadSegment(p1);
break;
case RNP:
Sp = MemRd(WordIndexed(Mp,MS_SP));
ret(FetchUB());
break;
case RBP:
Sp = MemRd(WordIndexed(Mp,MS_SP));
Base=Pop();
MemWr(STKBASE, Base);
if ( (Base<Kp) || (Base>BaseMp))
panic("RBP: Base %04x out of range", Base);
ret(FetchUB());
break;
case CSP: /* CSP, Call Standard Procedure */
switch(FetchUB())
{
case CSP_IOC:
if (MemRd(IORSLT))
XeqError(XUIOERR);
break;
case CSP_NEW:
ClrGDirP();
w=Pop();
MemWr(Pop(),Np);
Np=WordIndexed(Np, w);
StackCheck();
break;
case CSP_MVL:
{
word Len = Pop();
Integer DstOffset = PopInteger();
word Dst = Pop();
Integer SrcOffset = PopInteger();
word Src = Pop();
MoveLeft(Dst, DstOffset, Src, SrcOffset, Len);
}
break;
case CSP_MVR:
{
word Len = Pop();
Integer DstOffset = PopInteger();
word Dst = Pop();
Integer SrcOffset = PopInteger();
word Src = Pop();
MoveRight(Dst, DstOffset, Src, SrcOffset, Len);
}
break;
case CSP_XIT:
{
word ProcNo=Pop();
word SegNo=Pop();
word xMp=Mp;
word xSeg=Seg;
word xJTab=JTab;
Ipc=ProcExitIpc(xJTab);
while ( (ProcNumber(xJTab) != ProcNo) ||
(SegNumber(xSeg) != SegNo) )
{
if (!xMp ||
!(xJTab = MemRd(WordIndexed(xMp, MS_JTAB))) ||
!(xSeg = MemRd(WordIndexed(xMp, MS_SEG))) )
XeqError(XNOEXIT);
MemWr(WordIndexed(xMp, MS_IPC), ProcExitIpc(xJTab));
xMp = MemRd(WordIndexed(xMp, MS_DYN));
}
}
break;
case CSP_UREAD:
{
word p1, p2, p3, p4, p5, p6;
p6=Pop();p5=Pop();p4=Pop();
p3=Pop();p2=Pop();p1=Pop();
UnitRead(p1,p2,p3,p4,p5,p6);
}
break;
case CSP_UWRITE:
{
word p1, p2, p3, p4, p5, p6;
p6=Pop();p5=Pop();p4=Pop();
p3=Pop();p2=Pop();p1=Pop();
UnitWrite(p1,p2,p3,p4,p5,p6);
}
break;
case CSP_TIM:
{
struct timeval tv;
#ifndef _AMIGA
if (gettimeofday(&tv, NULL) <0)
{
perror("gettimeofday");
MemWr(Pop(),0);
MemWr(Pop(),0);
MemWr( LOWTIME, 0 );
MemWr( HIGHTIME, 0 );
}
else
{
tv.tv_sec = (tv.tv_usec*60*TIME_SCALE/1000000)
+ tv.tv_sec*60*TIME_SCALE;
MemWr( Pop(), (tv.tv_sec>> 0)&0xffff );
MemWr( LOWTIME, (tv.tv_sec>> 0)&0xffff );
MemWr( Pop(), (tv.tv_sec>>16)&0xffff );
MemWr( HIGHTIME, (tv.tv_sec>>16)&0xffff );
}
#endif
}
break;
#ifdef CSP_IDS
case CSP_IDS:
{
word BufPtr = Pop();
word Arg2Ptr = Pop();
CspIdSearch(BufPtr, Arg2Ptr);
}
break;
#endif
#ifdef CSP_TRS
case CSP_TRS:
{
word TokenBuf = Pop();
word ResultPtr = Pop();
word NodePtr = Pop(); /* initialize with root node addr */
Push(CspTreeSearch(TokenBuf, ResultPtr, NodePtr));
}
break;
#endif
case CSP_FLC:
{
word ch = Pop();
word Len = Pop();
Integer Offset = PopInteger();
word Addr = Pop();
if (!(Len&0x8000))
while (Len--)
MemWrByte(Addr, Offset++, ch);
}
break;
case CSP_SCN:
{
word Dummy = Pop();
Integer Offset = PopInteger();
word Buf = Pop(); /* Buffer Address */
word ch = Pop(); /* zu suchendes Zeichen */
word match = Pop(); /* 0 suche nach ==ch,
!=0: Suche nach !=ch */
word limit = Pop(); /* Limit */
word res;
if (limit&0x8000)
{
limit=0x10000-limit;
for (res=0; res<limit; res++)
if (MemRdByte(Buf, Offset-res)!=ch)
{ if (match) break; }
else
{ if (!match) break; }
Push(0x10000-res);
}
else
{
for (res=0; res<limit; res++)
if (MemRdByte(Buf, Offset+res)!=ch)
{ if (match) break; }
else
{ if (!match) break; }
Push(res);
}
}
break;
case CSP_USTAT:
{
word Dummy = Pop();
Integer Offset = PopInteger();
word Addr = Pop();
word Unit = Pop();
UnitStat(Unit, Addr, Offset, Dummy);
}
break;
#ifdef CSP_LDSEG
case CSP_LDSEG:
CspLoadSegment(Pop());
break;
#endif
#ifdef CSP_ULDSEG
case CSP_ULDSEG:
CspUnloadSegment(Pop());
break;
#endif
case CSP_TRC:
f=PopReal();
if (f<0)
Push(ceil(f));
else
Push(floor(f));
break;
case CSP_RND:
Push(rint(PopReal()));
break;
case CSP_MRK:
ClrGDirP();
MemWr(Pop(),Np);
break;
case CSP_RLS:
Np=MemRd(Pop());
StackCheck();
MemWr(GDIRP, NIL);
break;
case CSP_IOR:
Push(MemRd(IORSLT));
break;
case CSP_UBUSY:
Push(UnitBusy(Pop()));
break;
case CSP_POT:
{
float PwrOfTen[]={1e0, 1e1, 1e2, 1e3, 1e4, 1e5,
1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17,
1e18, 1e19, 1e20, 1e21, 1e22, 1e23,
1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31, 1e32, 1e33, 1e34, 1e35,
1e36, 1e37, 1e38, 1e39};
int Value=PopInteger();
if ( (Value<0) || (Value>39) )
PushReal(0);/* WWW: XeqError(XINVNDX); */
else
PushReal(PwrOfTen[Value]);
}
break;
case CSP_UWAIT:
UnitWait(Pop());
break;
case CSP_UCLEAR:
UnitClear(Pop());
break;
case CSP_HLT:
return;
break;
case CSP_MAV:
if (MemRd(GDIRP))
w=Kp-MemRd(GDIRP);
else
w=Kp-Np;
Push(w/2);
break;
default:
XeqError(XNOTIMP);
}
break;
case BPT:
p1 = FetchB();
if ( (MemRd(BUGSTATE)>=3) ||
(p1 == MemRd(BRKPTS0)) ||
(p1 == MemRd(BRKPTS1)) ||
(p1 == MemRd(BRKPTS2)) ||
(p1 == MemRd(BRKPTS3)) )
XeqError(XBRKPNT);
break;
case XIT:
return;
XeqError(XHLTBPT);
break;
case NOP:
break;
default:
XeqError(XNOTIMP);
break;
}
}
}
word LookupFile(word Unit, const char *Name)
{
int i;
DiskRead(Unit, Np, 0, 2048, 2);
if (MemRd(IORSLT))
return(0);
for (i=0;i<MemRd(WordIndexed(Np, 8));i++)
{
word Entry=WordIndexed(Np, 13+13*i);
int len;
for (len=0; len<MemRdByte(WordIndexed(Entry, 3),0); len++)
if (toupper(MemRdByte(WordIndexed(Entry, 3),1+len)) !=
toupper(Name[len]))
goto next;
if (Name[len])
continue;
return(MemRd(WordIndexed(Entry,0)));
next:
;
}
return(0);
}
/* Das Segment 0 ist aufgespalten, und die Zeiger im
Procedure-Dictionary sind so korrigiert worden, dass nach dem Laden
der beiden Hälften an die jeweils "richtige" Addresse die Zeiger
korrekt sind.
Diese Routine korrigiert die Zeiger im Segment-Dictionary. Dazu
ermittelt sie zuerst die Addresse, an der die zwiete hälfte
eigentlich geladen werden sollte. Danach wird ein Offset ermittelt,
mit dem die Zeiger in die zweite Hälfte korrigiert werden müssen. */
static void FixupSeg0(word LoadAddr)
{
word Seg=SegDict[0].Seg;
word SegBase=SegDict[0].SegBase;
word Addr;
word Offset;
int i;
Addr=0;
for (i=1;i<=SegNumProc(Seg); i++)
{
word JTab=Proc(Seg, i);
if (JTab<SegBase)
if (JTab>Addr)
Addr=JTab;
}
if (!Addr)
return; /* no Fixup needed */
Addr=WordIndexed(Addr,1);
Offset=LoadAddr-Addr;
if (!Offset)
return;
for (i=1;i<=SegNumProc(Seg); i++)
{
word JTab=Proc(Seg, i);
if ( (JTab<SegBase) )
{
Addr=WordIndexed(Seg,-i);
#ifdef WORD_MEMORY
MemWr(Addr, MemRd(Addr)-2*Offset);
#else
MemWr(Addr, MemRd(Addr)-Offset);
#endif
}
}
}
static void load(word Unit, word BlockNo)
{
int i;
DiskRead(Unit, Np, 0, 512, BlockNo);
if (MemRd(IORSLT))
return;
/* Erzeuge das Segment Dictionary */
for (i=0;i<16;i++)
{
word CodeAddr=MemRd(WordIndexed(Np, 2*i))+BlockNo;
word CodeLeng=MemRd(WordIndexed(Np, 2*i+1));
word SegInfo =MemRd(WordIndexed(Np, i+0x80));
assert (!(CodeLeng&1));
if (CodeAddr && CodeLeng)
{
int SegNo=SegInfo&0xff;
if (SegInfo&0x0f00)
{
MemWr(SEG_UNIT(SegNo), Unit);
MemWr(SEG_BLOCK(SegNo), CodeAddr);
MemWr(SEG_SIZE(SegNo), CodeLeng);
}
if (SegNo==0)
{
if (!SegDict[0].UseCount)
{
SegDict[0].UseCount++;
SegDict[0].OldKp=Kp;
SegDict[0].Seg=WordIndexed(Kp,-1);
#ifdef WORD_MEMORY
Kp-=CodeLeng/2;
#else
Kp-=CodeLeng;
#endif
SegDict[0].SegBase=Kp;
DiskRead(Unit, Kp, 0, CodeLeng, CodeAddr);
}
else
{
#ifndef WORD_MEMORY
#ifdef APPLE_SEG0_LOAD_GAP
if (AppleCompatibility)
{
Kp-=APPLE_SEG0_LOAD_GAP;
assert (Syscom>=Kp );
assert (WordIndexed(Syscom, SYSCOM_SIZE) <
Kp+APPLE_SEG0_LOAD_GAP );
}
#endif
#endif
FixupSeg0(Kp);
#ifdef WORD_MEMORY
Kp-=CodeLeng/2;
#else
Kp-=CodeLeng;
#endif
DiskRead(Unit, Kp, 0, CodeLeng, CodeAddr);
}
}
}
}
}
void LoadSystem(int RootUnit, const char *FileName)
{
int Unit=0;
int Block;
if ((Block=LookupFile(RootUnit, FileName)))
Unit=RootUnit;
else
for (Unit=4; Unit<MAX_UNIT; Unit++)
{
if (Unit==6)
Unit=9;
if ((Block=LookupFile(Unit, FileName)))
break;
}
if (!Block || !Unit)
panic("%s: not found", FileName );
load(Unit, Block);
if (MemRd(IORSLT))
panic("$s unit %d block %d: Ioerror %d",
FileName, Unit, Block, MemRd(IORSLT));
if (!SegDict[0].UseCount)
panic("%s: not a valid system, no segment 0", FileName );
call(SegDict[0].Seg, 1, NIL);
}
int main (int argc, char *argv[])
{
int i;
int Unit=4;
int UseXTerm=0;
int BatchFd=-1;
const char *SystemName="system.pascal";
memset(SegDict, 0, sizeof(SegDict));
MemInit();
DiskInit();
TraceProc=0;
TraceSeg=1;
while ((i=getopt(argc, argv,
#ifndef WORD_MEMORY
"a"
#endif
"b:gn:t:T:w:r:f:xV"))!=EOF)
switch(i)
{
#ifndef WORD_MEMORY
case 'a':
AppleCompatibility=1;
break;
#endif
case 'b':
if (!optarg || !*optarg)
{
fprintf(stderr,"-b option requires filename argument or '-' for stdin\n");
exit(1);
}
if (strcmp(optarg, "-")==0)
BatchFd=0;
else
if ((BatchFd=open(optarg, O_RDONLY))<0)
{
perror(optarg);
exit(1);
}
break;
case 'g':
TraceLevel=0x7fff;
break;
case 'n':
if (!optarg || !*optarg)
{
fprintf(stderr,"-n option requires filename argument or '-' for stdout\n");
exit(1);
}
SystemName=optarg;
break;
case 't':
if (!optarg || !*optarg)
{
fprintf(stderr,"-t option requires filename argument\n");
exit(1);
}
if (strcmp(optarg,"-")==0)
TraceFile=fdopen(dup(1), "w");
else
{
if (!(TraceFile=fopen(optarg,"w")))
{
perror(optarg);
exit(1);
}
}
break;
case 'T':
SetTrace(optarg);
break;
case 'w':
case 'r':
case 'f':
{
enum DiskMode Mode;
switch(i)
{
case 'w': Mode=ReadWrite; break;
case 'r': Mode=ReadOnly; break;
case 'f': Mode=Forget; break;
}
if (!optarg || !*optarg)
{
fprintf(stderr,"-%c option requires filename argument\n", i);
exit(1);
}
if (DiskMount(Unit, optarg, Mode)<0)
exit(1);
Unit++;
if (Unit==6) Unit=9;
}
break;
case 'x':
UseXTerm++;
break;
case 'V':
fprintf(stderr, "%s, a UCSD p-code interperter version %s\n", argv[0], VERSION);
fprintf(stderr, "For updated versions check http://www.klebsch.de/.\n");
exit(0);
break;
}
TermOpen(UseXTerm, BatchFd);
#ifndef WORD_MEMORY
if (AppleCompatibility)
{
Np=APPLE_HEAP_BOT;
Kp=APPLE_KP_TOP;
Syscom=APPLE_SYSCOM;
}
else
#endif
{
Np=HEAP_BOT;
Kp=KP_TOP;
Kp=WordIndexed(Kp, -SYSCOM_SIZE);
Syscom=Kp;
}
Sp=SP_TOP;
Mp=Kp;
LoadSystem(4, SystemName);
BaseMp=Mp;
Sp=WordIndexed(Sp,1); /* SP korrigieren */
MemWr( LocalAddr(1), Syscom );
MemWr( GDIRP , NIL );
MemWr( SYSUNIT, 4 );
#ifndef WORD_MEMORY
if (AppleCompatibility)
{
MemWr( WordIndexed(Syscom,161), 0x4bd);
MemWr( WordIndexed(Syscom,166), 0x6);
MemWrByte( WordIndexed(Syscom,169), 1, 0x81);/* Bitfeld mit
} unbekanntem Inhalt */
#endif
Processor();
DumpCore();
if (TraceFile)
fclose(TraceFile);
while (Unit>4)
{
Unit--;
if (Unit==8)
Unit=5;
DiskUmount(Unit);
}
TermClose();
return(0);
}